---
title: 高性能架构模式
---

面对海量用户和海量数据，单个数据库服务器已经难以满足业务需要，必须考虑数据库集群的方式来提升性能。高性能数据库集群的第一种方式是 “读写分离”，第二种方式是“数据库分片”。

## 读写分离架构
读写分离的基本原理是将数据库读写操作分散到不同的节点上：

![image-20230412131113854](https://img.wkq.pub/pic/image-20230412131113854.png)

* **主库负责处理事务性的增删改查、从库负责处理查询操作**，能够有效的避免由数据更新导致的行锁，使得整个系统的查询性能得到极大的改善。
* 读写分离是根据 SQL 语义的分析，将读操作和写操作分别路由至主库和从库。
* 通过 一主多从 的方式，可以将查询请求均匀的分散到多个数据副本，能够进一步的提升系统的处理能力。
* 使用 多主多从 的方式，不但能够替身系统的吞吐量，还能提高系统的可用性。






## CAP 定理

在一个分布式系统中，当涉及读写操作时，只能保证一致性（Consistance）、可用性（Availability）、分区容错性（Partition Tolerance）三者中的两个，另外一个必须被牺牲。

* C 一致性： 对某个指定的客户都安来说，读操作能够返回最新的写操作结果。
* A 可用性：非故障的节点在合理的时间内返回合理的响应（不是错误和超时的响应）
* P 分区容错性： 当出现网络分区后（丢包、连接中断或者拥塞），系统能够继续履行职责。



在实际的系统中，每个系统不可能只处理一种数据，而是包含多种类型的数据，**有的数据必须选择 CP，有的数据必须选择 AP，分布式系统理论上不可能存在 CA 架构**。



CAP 理论中的 C 在实践中是不可能完美实现的，即使无法做到强一致性，但应用可以采用适合的方式达到最终一致性。具有如下特点：

* 基本可用：分布式系统在出现故障时，允许损失部分可用性，即保证核心可用。
* 软状态：允许系统存在中间状态，而该中间状态不会影响系统整体可用性。这里的中间状态就是 CAP 理论中的数据不一致。
* 最终一致性：系统中的所有数据副本经过一定时间后，最终能够达到一致的状态。

## 数据库分片架构

#### 读写分离的问题

读写分离分散了数据库读写操作的压力，但没有分散存储压力，为了满足业务数据存储的需求，就需要将存储分散到多台数据库服务器上。

#### 数据分片

将存放在单一数据库中的数据分散地存放至多个数据库或表中，以达到提升性能瓶颈以及可用性的效果。数据分片其实就是对关系型数据库进行分库和分表。数据分片的方式又分为垂直分片和水平分片。

###  垂直分片

#### 垂直分库

按照业务拆分的方式称为垂直分片，又称为纵向拆分，它的核心是专库专用。在拆分之前，一个数据库由多个数据表构成，每个表对应着不同的业务。而拆分之后，则是按照业务将表进行分类，分布到不同的数据库中，从而将压力分散至不同的数据库。

![image-20230412211512788](https://img.wkq.pub/pic/image-20230412211512788.png)

垂直拆分可以缓解数据量和访问量带来的问题，但无法根治。**如果垂直拆分之后，表中的数据量依然拆过单节点所能承载的阈值，则需要水平分片来进一步处理。**

:::tip

海量数据有可能是单表数据过多。

:::

#### 垂直分表

垂直分表适合将表中的某些列，或者是占用了大量空间的列拆分出去。

假设我们是一个婚恋网站，用户在筛选其他用户的时候，主要是用 age 和 sex 两个字段进行查询，而 nickname 和 description 两个字段主要用于展示，一般不会在业务查询中用到。description 本身又比较长，因此我们可以将这两个字段独立到另外一张表中，这样在查询 age 和 sex 时，就能带来一定的性能提升。

垂直分表引入的复杂性主要体现在表操作的数量要增加。例如，原来只要一次查询就可以获取 name、age 、sex、nickname、description，现在需要两次查询，一次查询获取 name、age、sex，另外一次查询获取获取 nickname、description。

![image-20230412214217263](https://img.wkq.pub/pic/image-20230412214217263.png)

水平分表适合行数特别大的表，水平分表属于水平切分。

### 水平分片

水平分片又称为横向分片。相对于垂直分片，它不在将数据按照业务逻辑分类，而是通过某个字段（或者某几个字段），根据某种规则将数据分散至多个库或表中，每个分片仅包含数据的一部分。例如：根据主键分片，偶数主键的记录放入 0 库（或表），奇数主键的记录放入 1 库（或表），如下图所示：



![image-20230412214854739](https://img.wkq.pub/pic/image-20230412214854739.png)

单表进行切分后，是否将多个表分散在不同的数据库服务器中，可以根据实际的切分效果来确定。

* **水平分表**： 单表切分为多表后，新的表即使在同一个数据库服务器中，也可能带来可观的性能提升，如果性能满足业务要求，可以不拆分到多台数据库服务器，比如业务分库也会引入很多复杂性；
* **水平分库**：如果单表拆分为多表后，单台服务器依然无法满足性能要求，那就需要将多个表分散在不同的数据库服务器中。



:::tip 阿里巴巴Java开发手册

单表行数超过 500 万行或者单表容量吃啊过 2 GB，才推荐进行分库分表。

说明：如果预计三年后的数据量根本达不到这个级别，请不要在创建表时就分库分表。

:::

## 读写分离和数据分片架构

![image-20230412220222625](https://img.wkq.pub/pic/image-20230412220222625.png)

## 实现方式

读写分离和数据分片具体的实现方式一般有两种：程序代码封装和中间件封装。

### 程序代码封装

程序代码封装指在代码中抽象出一个数据访问层（或中间层封装），实现读写操作分离和数据库服务器连接的管理。

**其基本架构是：**以读写分离为例

![image-20230412221034860](https://img.wkq.pub/pic/image-20230412221034860.png)

### 中间件封装

中间件封装指的是独立一套系统出来，实现读写操作分离和数据库服务器连接的管理。对于业务服务器来说，访问中间件和访问数据库是没有去别的，在业务服务器看来，中间件就是一个数据库服务器。

**基本架构是：** 以读写分离为例

![image-20230412221255749](https://img.wkq.pub/pic/image-20230412221255749.png)

### 常用解决方案

Apache Sharding Sphere （程序级别和中间件级别）

MyCat(数据库中间件)

